#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "protocol.h"

typedef struct node {
	int source;
	int destination;
	int weight;
} Node;

int   **nodes;
int   **weights;
int   **external;
Node  **connections;
int   totalConnections = 0;

void printNodes(int *node) {
	int i = 0;
	for(i = 1; i < node[0]; i = i+2) {
		printf("%d|%c:%d ", i, node[i], node[i+1]);
	}
}

void printWeights(int p, int *weight) {
	int i = 0;
	for(i = 1; i < weight[0]; i += 2) {
		if(weight[i] == -1)
			printf("%d|%d:%d ", i, weight[i], weight[i+1]);
		else
			printf("%d|%c:%d ", i, nodes[p][weight[i]], weight[i+1]);
	}
}

void printExternal(int *extrn) {
	int i = 0;
	for(i = 1; i < extrn[0]; i += 3) {
		printf("%d|%c:%d:%d ", i, extrn[i], extrn[i+1], extrn[i+2]);
	}
}

void readNodes(FILE *inputFile, int *node) {
	int i = 0;
	for(i = 1; i < node[0]; i = i+2) {
		fscanf(inputFile, "%c\n", node+i);
	}
}

void readConnections(FILE *inputFile) {
	int src = 0;
	int dest = 0;
	int weight = 0 ;
	int count = 0;
	fscanf(inputFile, "%d\n", &count);

	// These are bi-directional graphs
	connections = malloc(count*sizeof(Node*)*2);
	totalConnections = count*2;

	int i = 0;
	for(i = 0; i+1 < totalConnections; i = i+2) {
		fscanf(inputFile, "%c %c %d\n", &src, &dest, &weight);
		connections[i] = malloc(sizeof(Node));
		connections[i]->source = src;
		connections[i]->destination = dest;
		connections[i]->weight = weight;
		connections[i+1] = malloc(sizeof(Node));
		connections[i+1]->source = dest;
		connections[i+1]->destination = src;
		connections[i+1]->weight = weight;
	}
}

// Finds the process that owns a given node
int findOwner(int procCount, int dest) {
	int i = 0;
	for(i = 0; i < procCount; i++) {
		int j = 0;
		for(j = 1; j < nodes[i][0]; j += 2) {
			if(nodes[i][j] == dest) {
				return i;
			}
		}
	}
	return -1;
}

void findConnections(int procCount) {
	int weightBuff[totalConnections*2];
	int externBuff[totalConnections*3];
	int weightCount = 0;
	int externCount = 0;

	// Loop for each process
	int proc = 0;
	for(proc = 0; proc < procCount; proc++) {
		// Slot 0 is reserved for length
		weightCount = 1;
		externCount = 1;
		
		// Loop over each node owned by the process
		int i = 0;
		for(i = 1; i < nodes[proc][0]; i = i+2) {
			// Store the index of first weight
			nodes[proc][i+1] = weightCount;
			// Find all connections of that node
			int j = 0;
			for(j = 0; j < totalConnections; j++) { 
				// If the connection source is owened by process
				if(connections[j]->source == nodes[proc][i]) {
					// Check if destination is owned
					_Bool owned = 0;
					int k = 0;
					for(k = 1; k < nodes[proc][0]; k = k+2) {
						if(connections[j]->destination == nodes[proc][k]) {
							owned = 1;
							break;
						}
					}
					if(owned) {
						weightBuff[weightCount] = k;
						weightBuff[weightCount+1] = connections[j]->weight;
						weightCount += 2;
					}else {
						weightBuff[weightCount] = -1;
						weightBuff[weightCount+1] = externCount;
						weightCount += 2;

						// Find the proc that owns the node
						int owner = findOwner(procCount, connections[j]->destination);
						if(owner == -1) {
							fprintf(stderr, "No owner for node %c found\n", 
									  connections[j]->destination);
							exit(13);
						}

						externBuff[externCount] = connections[j]->destination;
						externBuff[externCount+1] = owner;
						externBuff[externCount+2] = connections[j]->weight;
						externCount += 3;
					}
				}
			}
		}

		// Store length in array
		weightBuff[0] = weightCount;
		externBuff[0] = externCount;

		// Copy buffer into properly sized array
		weights[proc] = malloc(weightCount*sizeof(int));
		external[proc] = malloc(externCount*sizeof(int));

		memcpy(weights[proc], weightBuff, weightCount*sizeof(int));
		memcpy(external[proc], externBuff, externCount*sizeof(int));
	}
}

	int procCount = 1;

void fillArrays() {
	char  *fileName = "test1.graph";
	FILE  *inputFile = fopen(fileName, "r");

	int nodeCount = 0;
	int nodePerProc = 0;
	int remainder = 0;

   if ( inputFile == NULL )
   {  fprintf (stderr, "Open failed for %s\n", fileName); exit(666);  }
	
	fscanf(inputFile, "%d\n", &nodeCount);

	nodePerProc = nodeCount/procCount;
	remainder = nodeCount%procCount;

	/*
	 * Creat the NODES array for all processors
	 */
	nodes = malloc(procCount*sizeof(int*));
	int i = 0;
	for(i = 0; i < procCount; i++) {
		int count = nodePerProc + ((remainder-- > 0) ? 1 : 0);
		// Add one to hold the length of the array
		nodes[i] = malloc((1+count*2)*sizeof(int));
		nodes[i][0] = 1+count*2;
		readNodes(inputFile, nodes[i]);
	}

	readConnections(inputFile);
	fclose(inputFile);

	// Construct the weights and external array
	weights = malloc(procCount*sizeof(int*));
	external = malloc(procCount*sizeof(int*));

	findConnections(procCount);

	/*
	 * Print the node array
	 * Print the weight array
	 * Print the external array
	 */
	for(i = 0; i < procCount; i++) {
		printf("Process %d\n", i);
		printNodes(nodes[i]);
		printf("\n");
		printWeights(i, weights[i]);
		printf("\n");
		printExternal(external[i]);
		printf("\n\n");
	}
}

void graphCleanup() {
	// Free memory
	int i = 0;
	for(i = 0; i < procCount; i++) {
		free(nodes[i]);
		free(weights[i]);
		free(external[i]);
	}
	free(nodes);
	free(weights);
	free(external);

	for(i = 0; i < totalConnections; i++) {
		free(connections[i]);
	}
	free(connections);
}
